package com.niodata.dp.task;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ParamReference {

  private static final String SPLIT_PATTERN =
      "\\$\\{JOB\\.|\\.OUT\\.|\\.IN\\.|\\$\\{APP\\.|\\.VAR\\.";
  public static String PARAM_OUTPUT_REF_PATTERN = "^\\$\\{JOB..+.OUT..+\\}$";
  public static String PARAM_INPUT_REF_PATTERN = "^\\$\\{JOB..+.IN..+\\}$";
  public static String PARAM_APP_REF_PATTERN = "^\\$\\{APP..+.VAR..+\\}$";
  private String appCode;
  private String jobName;
  private String varName;
  private boolean input;
  private boolean output;
  private boolean app;
  private String origin;
  private boolean refered;


  /**
   * parse param references.
   *
   * @param src origin string
   * @return param references
   */
  public static List<ParamReference> paramReferences(String src) {
    Pattern pattern = Pattern.compile("\\$\\{.+?\\}");
    Matcher matcher = pattern.matcher(src);
    List<int[]> matches = new ArrayList<>();
    while (matcher.find()) {
      int start = matcher.start();
      int end = matcher.end();
      matches.add(new int[]{start, end});
    }
    List<ParamReference> list = new ArrayList<>();
    if (matches.size() == 0) {
      ParamReference ref = new ParamReference();
      ref.origin = src;
      ref.refered = false;
      list.add(ref);
    } else {
      for (int i = 0; i < matches.size(); i++) {
        ParamReference ref = new ParamReference();
        ref.refered = true;
        ref.origin = src.substring(matches.get(i)[0], matches.get(i)[1]);
        ref = parseReference(ref);
        list.add(ref);
        if (i != matches.size() - 1 && matches.get(i)[1] < matches.get(i + 1)[0]) {
          ParamReference ref1 = new ParamReference();
          ref1.refered = false;
          int start = matches.get(i)[1];
          int end = matches.get(i + 1)[0];
          ref1.origin = src.substring(start, end);
          list.add(ref1);
        }
      }
      if (matches.get(0)[0] != 0) {
        ParamReference ref = new ParamReference();
        ref.refered = false;
        ref.origin = src.substring(0, matches.get(0)[0]);
        list.add(0, ref);
      }
      if (matches.get(matches.size() - 1)[1] != src.length()) {
        ParamReference ref = new ParamReference();
        ref.refered = false;
        ref.origin = src.substring(matches.get(matches.size() - 1)[1]);
        list.add(ref);
      }
    }
    return list;
  }


  private static ParamReference parseReference(ParamReference origin) {
    String referStr = origin.origin;
    if (referStr.matches(PARAM_OUTPUT_REF_PATTERN)) {
      referStr = referStr.substring(0, referStr.length() - 1);
      String[] ss = referStr.split(SPLIT_PATTERN, -1);
      //${APP.app1.VAR.var1}
      //${JOB.job1.IN.var1}
      //${JOB.job1.OUT.var1}
      origin.setOutput(true);
      origin.setVarName(ss[2]);
      origin.setJobName(ss[1]);
      origin.refered = true;
      return origin;
    } else if (referStr.matches(PARAM_INPUT_REF_PATTERN)) {
      referStr = referStr.substring(0, referStr.length() - 1);
      String[] ss = referStr.split(SPLIT_PATTERN, -1);
      origin.setInput(true);
      origin.setVarName(ss[2]);
      origin.setJobName(ss[1]);
      origin.refered = true;
      return origin;
    } else if (referStr.matches(PARAM_APP_REF_PATTERN)) {
      referStr = referStr.substring(0, referStr.length() - 1);
      String[] ss = referStr.split(SPLIT_PATTERN, -1);
      origin.setApp(true);
      origin.setAppCode(ss[1]);
      origin.setVarName(ss[2]);
      return origin;
    } else {
      origin.refered = false;
    }
    return origin;
  }

  /**
   * parse variable ref from string.
   *
   * @param referStr raw ref string
   * @return ref
   */
  @Deprecated
  public static ParamReference parseFrom(String referStr) {
    if (referStr.matches(PARAM_OUTPUT_REF_PATTERN)) {
      referStr = referStr.substring(0, referStr.length() - 1);
      String[] ss = referStr.split(SPLIT_PATTERN, -1);
      //${APP.app1.VAR.var1}
      //${JOB.job1.IN.var1}
      //${JOB.job1.OUT.var1}
      ParamReference reference = new ParamReference();
      reference.setOutput(true);
      reference.setVarName(ss[2]);
      reference.setJobName(ss[1]);
      reference.refered = true;
      return reference;
    } else if (referStr.matches(PARAM_INPUT_REF_PATTERN)) {
      referStr = referStr.substring(0, referStr.length() - 1);
      String[] ss = referStr.split(SPLIT_PATTERN, -1);
      ParamReference reference = new ParamReference();
      reference.setInput(true);
      reference.setVarName(ss[2]);
      reference.setJobName(ss[1]);
      reference.refered = true;
      return reference;
    } else if (referStr.matches(PARAM_APP_REF_PATTERN)) {
      referStr = referStr.substring(0, referStr.length() - 1);
      String[] ss = referStr.split(SPLIT_PATTERN, -1);
      ParamReference reference = new ParamReference();
      reference.setApp(true);
      reference.setAppCode(ss[1]);
      reference.setVarName(ss[2]);
      return reference;
    }
    return null;
  }


  public String getAppCode() {
    return appCode;
  }

  public void setAppCode(String appCode) {
    this.appCode = appCode;
  }

  public String getJobName() {
    return jobName;
  }

  public void setJobName(String jobName) {
    this.jobName = jobName;
  }

  public String getVarName() {
    return varName;
  }

  public void setVarName(String varName) {
    this.varName = varName;
  }

  public boolean isInput() {
    return input;
  }

  public void setInput(boolean input) {
    this.input = input;
  }

  public boolean isOutput() {
    return output;
  }

  public void setOutput(boolean output) {
    this.output = output;
  }

  public boolean isApp() {
    return app;
  }

  public void setApp(boolean app) {
    this.app = app;
  }

  public boolean isRefered() {
    return refered;
  }

  public String getOrigin() {
    return origin;
  }
}
